package config;


import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.annotation.AnnotationUtils;
import processor.AnnotationProcessor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author jiang
 */
public class CglibProxy<T> implements MethodInterceptor {

    private Set<AnnotationProcessor> annotationProcessors;

    public CglibProxy(Set<AnnotationProcessor> annotationProcessors) {
        this.annotationProcessors = annotationProcessors;
    }

    public T newInstall(Object object) {
        return (T) Enhancer.create(object.getClass(), this);
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        List<AnnotationProcessor> list = new ArrayList<>();
        for (AnnotationProcessor annotationProcessor : annotationProcessors) {
            Class annotationClass = annotationProcessor.targetAnnotation();
            Annotation annotation1 = AnnotationUtils.findAnnotation(method, annotationClass);
            if (annotation1 != null) {
                list.add(annotationProcessor);
            }
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                Annotation annotation2 = AnnotationUtils.findAnnotation(parameter, annotationClass);
                if (annotation2 != null) {
                    objects[i] = annotationProcessor.parameterInit(annotationProcessor.targetAnnotation(), o, method, parameter, objects[i]);
                }
            }
        }
        for (AnnotationProcessor annotationProcessor : list) {
            annotationProcessor.methodBefore(annotationProcessor.targetAnnotation(), o, method, objects);
        }
        Object ret = methodProxy.invokeSuper(o, objects);
        for (AnnotationProcessor annotationProcessor : list) {
            Object o1 = annotationProcessor.methodAfter(annotationProcessor.targetAnnotation(), o, method, ret);
            ret = o1;
        }

        return ret;
    }
}